package service

import (
	"MyWeb/internal/cache"
	"MyWeb/internal/models"
	"MyWeb/internal/pkg/shortcode"
	"errors"
	"github.com/gin-gonic/gin"
	"time"
)

type UrlMapRepo interface {
	CreateSHortURL(url *models.UrlMap) error
	FindShortURL(UserShortURL string) error
	FindLongURL(ShortURL string) (*models.UrlMap, error)
	IsExpiredAt(url *models.UrlMap) bool
}

// shortCodeGenerator 短链接生成接口
type shortCodeGenerator interface {
	CreateShortCode() string
}

// Cacher 缓存接口
type Cacher interface {
	SetURL(c *gin.Context, url *models.CacheSL) error
	GetURL(c *gin.Context, shortUrl string) (*models.CacheSL, error)

	// BloomFliter
	SaveToBloomFilter(ctx *gin.Context, shortUrl string) error
	FindShortUrlFormBloomFilter(ctx *gin.Context, shortUrl string) (bool, error)
}

type UrlService struct {
	baseURL         string
	defaultDuration time.Duration
	// 抽象出三个接口：1.数据交互层的接口  2.生成最短算法的接口， 3.缓存接口
	query              UrlMapRepo
	shortCodeGenerator shortCodeGenerator
	cache              Cacher
}

func NewUrlService(repo UrlMapRepo) *UrlService {
	return &UrlService{
		query:              repo,
		defaultDuration:    1 * time.Hour,
		baseURL:            "http://localhost:8080", //处理器的地址
		shortCodeGenerator: shortcode.NewSnowURL(),
		cache:              cache.NewShortCache(),
	}
}

// CreateURL 请求处理函数 -> 处理并返回响应
func (s *UrlService) CreateURL(c *gin.Context, req models.UrlRequest) (*models.UrlResponse, error) {
	// 处理请求数据
	var UrlMap models.UrlMap

	//判断用户是否自定义
	if req.CustomCode != "" {
		shortUrl := req.CustomCode
		//1.先判断bloomFilter有没有这个
		//ok,err:=s.cache.FindShortUrlFormBloomFilter(c,shortUrl)
		//如果直接结束，如果不ok查数据库
		//2.判断在数据库是否存在这个短链,存在则直接return,不在写入数据库

		if err := s.query.FindShortURL(shortUrl); err != nil {
			return nil, err
		}
		UrlMap.ShortUrl = shortUrl
		UrlMap.UserDefined = true
	} else {
		// 2.用户不自定义，则算法生成
		shortUrl, err := s.getShortURL(0)
		if err != nil {
			return nil, err
		}
		UrlMap.ShortUrl = shortUrl
		UrlMap.UserDefined = false
	}

	//处理过期时间
	if req.UsedTime == nil {
		UrlMap.ExpiresAt = time.Now().Add(s.defaultDuration)
	} else {
		UrlMap.ExpiresAt = time.Now().Add(time.Hour * time.Duration(*req.UsedTime))
	}

	// 插入数据库
	UrlMap.LongUrl = req.LongUrl
	UrlMap.GetNowTime()
	if err := s.query.CreateSHortURL(&UrlMap); err != nil {
		return nil, err
	}

	//存入缓存
	//在此之前先插入布隆过滤器
	//err := s.cache.SaveToBloomFilter(c, UrlMap.ShortUrl)
	//if err != nil {
	//	return nil, err
	//}
	SL := &models.CacheSL{
		ShortURL:  UrlMap.ShortUrl,
		LongURL:   UrlMap.LongUrl,
		ExpiredAt: UrlMap.ExpiresAt,
	}
	if err := s.cache.SetURL(c, SL); err != nil {
		return nil, err
	}

	// 处理响应
	return &models.UrlResponse{
		// 只在resp的时候对其加上基础链接即可
		ExpiresAt: UrlMap.ExpiresAt,
		ShortUrl:  s.baseURL + "/" + UrlMap.ShortUrl,
	}, nil
}

func (s *UrlService) GetLongURL(c *gin.Context, ShortUrl string) (string, error) {
	//先查BloomFilter
	//ok,errBloo := s.cache.FindShortUrlFormBloomFilter(c, ShortUrl)
	//if errBloo != nil {
	//	return "", errBloo
	//}
	//if ok {
	//	就查询缓存，否则就不查询缓存
	//}
	// 查询缓存（redis），判断是否存在
	CaSL, err := s.cache.GetURL(c, ShortUrl)
	if err != nil {
		return "", err
	}
	if CaSL != nil {
		return CaSL.LongURL, nil
	}

	// 查询磁盘（mysql），获取
	urlMap, err2 := s.query.FindLongURL(ShortUrl)
	if err2 != nil {
		return "", err2
	} else {
		// 缓存没有，但是磁盘有，就将他再次存入缓存
		SL := &models.CacheSL{
			ShortURL:  urlMap.ShortUrl,
			LongURL:   urlMap.LongUrl,
			ExpiredAt: urlMap.ExpiresAt,
		}
		if err2 := s.cache.SetURL(c, SL); err2 != nil {
			return "", err2
		}
		return urlMap.LongUrl, nil
	}
}

func (s *UrlService) getShortURL(n int) (string, error) {
	if n > 5 {
		return "", errors.New("重试次数过多,生成的短链重复率过高")
	}

	shortURL := s.shortCodeGenerator.CreateShortCode()
	if err := s.query.FindShortURL(shortURL); err == nil {
		return shortURL, nil
	}

	return s.getShortURL(n + 1)
}
